﻿using DefaultDspPluginsLib;
using SoundEngine3;
using SoundEngine3.AudioProcessing;
using SoundEngine3.DspPlugins;
using SoundEngine3.DspPlugins.Meta;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ZquenceStudio3.Core;
using ZquenceStudio3.Core.DspPlugins;
using ZquenceStudio3.DspPlugins;

namespace DefaultDspPlugins.Effects
{
    /// <summary>
    /// Reference: https://christianfloisand.wordpress.com/2014/07/16/dynamics-processing-compressorlimiter-part-3/
    /// </summary>
    public class CompressorDescription : IDspPlugin
    {
        public DspPluginInfo GetDspPluginInfo()
        {
            return new DspPluginInfo("Compressor", DspType.Effect);
        }

        public BaseDspPluginInstance CreateDsp(ZquenceStudio3Context context, DspPluginInfo info)
        {
            return new Compressor(context, info);
        }
    }

    public class Compressor : CoreDspEffectPlugin
    {
        public struct CompressorData
        {
            public int n;
            public double threshold;
            public double slope;
            public int sr;
            public double tla;
            public double twnd;
            public double tatt;
            public double trel;
            public double env;
            public double inputGain;
            public double outputGain;
        }

#if !Portable
        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Compressor_Process(ref CompressorData data, ref double in_left, ref double in_right, ref double out_left, ref double out_right);
#endif

        private SimpleParameter mAttack;
        private SimpleParameter mRelease;
        private SimpleParameter mLookahead;
        private SimpleParameter mRMS;
        private SimpleParameter mThreshold;
        private SimpleParameter mSlope;
        private SimpleParameter mInputGain;
        private SimpleParameter mOutputGain;

        public Compressor(ZquenceStudio3Context soundEngineContext, DspPluginInfo dspPlugin)
            : base(soundEngineContext, dspPlugin)
        {
            // Attack.
            mAttack = new SimpleParameter(this);
            mAttack.Label = "Attack ms";
            mAttack.MinValue = 0.01;
            mAttack.MaxValue = 10;
            mAttack.RawValue = 0.1;
            AddParameter(mAttack);

            // Release.
            mRelease = new SimpleParameter(this);
            mRelease.Label = "Release ms";
            mRelease.MinValue = 1;
            mRelease.MaxValue = 2000;
            mRelease.RawValue = 300;
            AddParameter(mRelease);

            // Lookahead.
            mLookahead = new SimpleParameter(this);
            mLookahead.Label = "Lookahead ms";
            mLookahead.MinValue = 1;
            mLookahead.MaxValue = 100;
            mLookahead.RawValue = 3;
            AddParameter(mLookahead);

            // RMS.
            mRMS = new SimpleParameter(this);
            mRMS.Label = "RMS window width ms";
            mRMS.MinValue = 1;
            mRMS.MaxValue = 10;
            mRMS.RawValue = 1;
            AddParameter(mRMS);

            // RMS.
            mThreshold = new SimpleParameter(this);
            mThreshold.Label = "Threshold %";
            mThreshold.MinValue = 0;
            mThreshold.MaxValue = 100;
            mThreshold.RawValue = 50;
            AddParameter(mThreshold);

            // RMS.
            mSlope = new SimpleParameter(this);
            mSlope.Label = "Slope %";
            mSlope.MinValue = 0;
            mSlope.MaxValue = 100;
            mSlope.RawValue = 50;
            AddParameter(mSlope);

            // Gain.
            mInputGain = new SimpleParameter(this);
            mInputGain.Label = "Input gain";
            mInputGain.MinValue = 0;
            mInputGain.MaxValue = 2;
            mInputGain.RawValue = 1;
            AddParameter(mInputGain);

            // Gain.
            mOutputGain = new SimpleParameter(this);
            mOutputGain.Label = "Output gain";
            mOutputGain.MinValue = 0;
            mOutputGain.MaxValue = 2;
            mOutputGain.RawValue = 1;
            AddParameter(mOutputGain);

            SupportAudioProcessing = true;
        }

        public override Version Version
        {
            get { return DspPluginInfo.DefaultVersion; }
        }

        public override string PluginInfo
        {
            get { return DspPluginInfo.DefaultPluginInfo; }
        }
        
        protected override void OnProcessEvents(List<SoundEngine3.MIDI.Meta.MidiMessage> midiMessages)
        {

        }

        // envelope
        CompressorData data;

        public override void Process(BaseAudioBuffer input, BaseAudioBuffer output)
        {
            double[] wav_in_left = input.Channels[0].Samples;     // signal
            double[] wav_in_right = input.Channels[1].Samples;     // signal
            double[] wav_out_left = output.Channels[0].Samples;     // signal
            double[] wav_out_right = output.Channels[1].Samples;     // signal

            //CompressorData data;
            data.n = wav_in_left.Length;          // N samples
            data.threshold = mThreshold.RawValue;  // threshold (percents)
            data.slope = mSlope.RawValue;      // slope angle (percents)
            data.sr = (int)SoundEngineContext.SampleRate;         // sample rate (smp/sec)
            data.tla = mLookahead.RawValue;        // lookahead  (ms)
            data.twnd = mRMS.RawValue;       // window time (ms)
            data.tatt = mAttack.RawValue;       // attack time  (ms)
            data.trel = mRelease.RawValue;       // release time (ms)
            data.inputGain = mInputGain.RawValue;
            data.outputGain = mOutputGain.RawValue;

            if (NativeSoundEngineDllImport.UseBenchmarking)
            {

                ZquenceStudio3.Core.Utils.ProfilerMeter.Start1("Compressor");
#if !Portable
                Effect_Compressor_Process(ref data, ref wav_in_left[0], ref wav_in_right[0], ref wav_out_left[0], ref wav_out_right[0]);
#endif
                ZquenceStudio3.Core.Utils.ProfilerMeter.Stop1("Compressor");
                ZquenceStudio3.Core.Utils.ProfilerMeter.Start2("Compressor");
                DefaultDspPluginsLib.Depricated.Effect_Compressor.Process(ref data, wav_in_left, wav_in_right, wav_out_left, wav_out_right);
                ZquenceStudio3.Core.Utils.ProfilerMeter.Stop2("Compressor");
            }
            else
            {
                if (NativeSoundEngineDllImport.UseNativeProcessing)
                {
#if !Portable
                    Effect_Compressor_Process(ref data, ref wav_in_left[0], ref wav_in_right[0], ref wav_out_left[0], ref wav_out_right[0]);
#endif
                }
                else
                {
                    DefaultDspPluginsLib.Depricated.Effect_Compressor.Process(ref data, wav_in_left, wav_in_right, wav_out_left, wav_out_right);
                }
            }
        }
    }
}
